package org.lds.scheduled.job.worker.scheduler.service;

import lombok.RequiredArgsConstructor;
import org.lds.scheduled.job.common.enums.TriggerRuleTypeEnum;
import org.lds.scheduled.job.common.exception.BusinessException;
import org.lds.scheduled.job.common.model.AddJobModel;
import org.lds.scheduled.job.worker.common.constant.WorkerConstant;
import org.lds.scheduled.job.worker.common.enums.WorkerErrorEnum;
import org.lds.scheduled.job.worker.scheduler.job.SchedulerCommonJob;
import org.quartz.*;
import org.springframework.stereotype.Service;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@RequiredArgsConstructor
@Service
public class SchedulerServiceImpl implements SchedulerService {
    private final Scheduler scheduler;

    @Override
    public JobDetail newJob(Map<String, Object> jobDataMap, String jobName, String groupName) {
        return JobBuilder.newJob(SchedulerCommonJob.class)
                .usingJobData(new JobDataMap(jobDataMap))
                .withIdentity(jobName, groupName)
                .build();
    }

    @Override
    public boolean deleteJob(String jobName, String groupName) {
        try {
            return scheduler.deleteJob(JobKey.jobKey(jobName, groupName));
        } catch (Exception e) {
            throw new BusinessException(WorkerErrorEnum.JOB_DELETE_FAIL);
        }
    }

    @Override
    public Trigger newTrigger(String triggerName, String groupName, int intervalInSeconds) {
        return TriggerBuilder.newTrigger()
                .withIdentity(triggerName, groupName)
                .startNow()// 立即生效
                .withSchedule(SimpleScheduleBuilder.simpleSchedule()
                        // 每隔intervalInSeconds秒执行一次
                        .withIntervalInSeconds(intervalInSeconds)
                        // 一直执行
                        .repeatForever()
                        // 错过触发后忽略
                        .withMisfireHandlingInstructionFireNow())
                .build();
    }

    @Override
    public Trigger newTrigger(String triggerName, String groupName, String cron) {
        try {
            return TriggerBuilder.newTrigger()
                    .withIdentity(triggerName, groupName)
                    .startNow()// 立即生效
                    .withSchedule(CronScheduleBuilder.cronSchedule(cron)
                            .withMisfireHandlingInstructionDoNothing())
                    .build();
        } catch (Exception e) {
            throw new BusinessException(WorkerErrorEnum.CRON_INVALID);
        }
    }

    @Override
    public Trigger newTrigger(String triggerName, String groupName, int type, String content) {
        return type == TriggerRuleTypeEnum.FIX_RATE.getType().intValue() ?
                newTrigger(triggerName, groupName, Integer.valueOf(content)) : newTrigger(triggerName, groupName, content);
    }

    @Override
    public Trigger updateTrigger(String triggerName, String triggerGroupName, int type, String content) {
        try {
            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);
            Trigger oldTrigger = scheduler.getTrigger(triggerKey);
            Trigger newTrigger = newTrigger(triggerName, triggerGroupName, type, content);

            if (Objects.isNull(oldTrigger)) return newTrigger;

            if (!needUpdate(oldTrigger, newTrigger)) return oldTrigger;

            scheduler.rescheduleJob(triggerKey, newTrigger);
            return newTrigger;
        } catch (Exception e) {
            throw new BusinessException(WorkerErrorEnum.JOB_UPDATE_FAIL);
        }
    }

    private boolean needUpdate(Trigger trigger1, Trigger trigger2) {
        if (trigger1 == null || trigger2 == null || trigger1 == trigger2) return false;
        if (trigger1 instanceof SimpleTrigger && trigger2 instanceof SimpleTrigger) {
            SimpleTrigger st1 = (SimpleTrigger) trigger1;
            SimpleTrigger st2 = (SimpleTrigger) trigger2;
            return st1.getRepeatInterval() != st2.getRepeatInterval();
        } else if (trigger1 instanceof CronTrigger && trigger2 instanceof CronTrigger) {
            CronTrigger ct1 = (CronTrigger) trigger1;
            CronTrigger ct2 = (CronTrigger) trigger2;

            return !(ct1.getCronExpression().equals(ct2.getCronExpression()) &&
                    ct1.getTimeZone().equals(ct2.getTimeZone()));
        }

        return false;
    }

    @Override
    public Date scheduleJob(JobDetail job, Trigger trigger) {
        try {
            return scheduler.scheduleJob(job, trigger);
        } catch (Exception e) {
            throw new BusinessException(WorkerErrorEnum.JOB_EXECUTE_FAIL);
        }
    }

    @Override
    public void scheduleJob(AddJobModel addJobModel) {
        try {
            String appName = addJobModel.getApp().getName();
            Trigger trigger = updateTrigger(addJobModel.getName(), appName, addJobModel.getTriggerRuleType(), addJobModel.getTriggerRuleContent());
            if (scheduler.checkExists(JobKey.jobKey(addJobModel.getName(), appName))) return;

            Map<String, Object> jobDataMap = new HashMap<>();
            jobDataMap.put(WorkerConstant.APP_NAME, appName);
            jobDataMap.put(WorkerConstant.JOB_NAME, addJobModel.getName());
            jobDataMap.put(WorkerConstant.APP_INSTANCES, addJobModel.getAppInstances());
            scheduler.scheduleJob(newJob(jobDataMap, addJobModel.getName(), appName), trigger);
        } catch (Exception e) {
            throw new BusinessException(WorkerErrorEnum.JOB_EXECUTE_FAIL);
        }
    }

}
